package chapt08.toon;
// Points.java
// OpenGL SuperBible
// Demonstrates OpenGL Primitive GL_POINTS
// Program by Richard S. Wright Jr.

import javax.media.opengl.GL2;
import javax.media.opengl.glu.GLU;

import java.nio.ByteBuffer;

import shared.*;

public class Toon
{
	static GLU glu = new GLU();
	
	// Vector pointing towards the light
	static float vLightDir[] = {-1.0f, 1.0f, 1.0f};
	
	// Rotation angle
	static float yRot = 0.0f;
	
	// Draw a torus (doughnut), using the current 1D texture for light shading
	static void toonDrawTorus(GL2 gl2, float majorRadius, float minorRadius,
		int numMajor, int numMinor, float vLightDir[])
	{
		float mModelViewMatrix[] = new float[16];
		float mInvertedLight[] = new float[16];
		float vNewLight[] = new float[3];
		float vNormal[] = new float[3];
		float majorStep = 2.0f * M3D.PI / numMajor;
		float minorStep = 2.0f * M3D.PI / numMinor;
		int i, j;
		
		// Get the modelview matrix
		gl2.glGetFloatv(GL2.GL_MODELVIEW_MATRIX, mModelViewMatrix, 0);
		
		// Instead of transforming every normal and then dotting it with
	    // the light vector, we will transform the light into object 
	    // space by multiplying it by the inverse of the modelview matrix
		M3D.invertMatrix44(mInvertedLight, mModelViewMatrix);
		M3D.transformVector3(vNewLight, vLightDir, mInvertedLight);
		vNewLight[0] -= mInvertedLight[12];
		vNewLight[1] -= mInvertedLight[13];
		vNewLight[2] -= mInvertedLight[14];
		M3D.normalizeVector(vNewLight);
		
		for(i = 0; i < numMajor; ++i)
		{
			double a0 = i * majorStep;
			double a1 = a0 + majorStep;
			float x0 = (float)Math.cos(a0);
			float y0 = (float)Math.sin(a0);
			float x1 = (float)Math.cos(a1);
			float y1 = (float)Math.sin(a1);
			
			gl2.glBegin(GL2.GL_TRIANGLE_STRIP);
			for(j = 0; j <= numMinor; ++j)
			{
				double b = j * minorStep;
				float c = (float)Math.cos(b);
				float r = minorRadius * c + majorRadius;
				float z = minorRadius * (float)Math.sin(b);
				
				// First point
				vNormal[0] = x0 * c;
				vNormal[1] = y0 * c;
				vNormal[2] = z / minorRadius;
				M3D.normalizeVector(vNormal);
				gl2.glNormal3fv(vNormal, 0);
				
				// Texture coordinate is set by intensity of light
				gl2.glTexCoord1f(M3D.dotProduct(vNewLight, vNormal));
				gl2.glVertex3f(x0 * r, y0 * r, z);
				
				// Second point
				vNormal[0] = x1 * c;
				vNormal[1] = y1 * c;
				vNormal[2] = z / minorRadius;
				M3D.normalizeVector(vNormal);
				gl2.glNormal3fv(vNormal, 0);
				
				// Texture coordinate is set by intensity of light
				gl2.glTexCoord1f(M3D.dotProduct(vNewLight, vNormal));
				gl2.glVertex3f(x1 * r, y1 * r, z);
			}
			
			gl2.glEnd();
		}
	}
	
	// This function does any needed initialization on the rendering
	// context.
	protected static void setup(GL2 gl2, int width, int height)
	{
		// Load a 1D texture with toon shaded values
	    // Green, greener...
		byte src[] = {
			(byte)0, (byte)32, (byte)0,
			(byte)0, (byte)64, (byte)0,
			(byte)0, (byte)128, (byte)0,
			(byte)0, (byte)192, (byte)0
		};
		ByteBuffer toonTable = ByteBuffer.wrap(src);
		
		// Bluish background
		gl2.glClearColor(0.0f, 0.0f, .50f, 1.0f);
		gl2.glEnable(GL2.GL_DEPTH_TEST);
		
		gl2.glEnable(GL2.GL_CULL_FACE);
		
		gl2.glTexEnvi(GL2.GL_TEXTURE_ENV, GL2.GL_TEXTURE_ENV_MODE, GL2.GL_DECAL);
		gl2.glTexParameteri(GL2.GL_TEXTURE_1D, GL2.GL_TEXTURE_MAG_FILTER, GL2.GL_NEAREST);
		gl2.glTexParameteri(GL2.GL_TEXTURE_1D, GL2.GL_TEXTURE_MIN_FILTER, GL2.GL_NEAREST);
		gl2.glTexParameteri(GL2.GL_TEXTURE_1D, GL2.GL_TEXTURE_WRAP_S, GL2.GL_CLAMP);
		gl2.glPixelStorei(GL2.GL_UNPACK_ALIGNMENT, 1);
		gl2.glTexImage1D(GL2.GL_TEXTURE_1D, 0, GL2.GL_RGB, 4, 0, GL2.GL_RGB, GL2.GL_UNSIGNED_BYTE, toonTable);
		
		gl2.glEnable(GL2.GL_TEXTURE_1D);
	}
	
	// Called to draw scene
	protected static void render(GL2 gl2, int w, int h)
	{
		float fAspect;
		
		// Prevent a divide by zero, when window is too short
		// (you cant make a window of zero width).
		if(h == 0)
			h = 1;
		
		gl2.glViewport(0, 0, w, h);
		
		fAspect = (float)w / (float)h;
		
		// Reset the coordinate system before modifying
		gl2.glMatrixMode(GL2.GL_PROJECTION);
		gl2.glLoadIdentity();
		
		// Set the clipping volume
		glu.gluPerspective(35.0f, fAspect, 1.0f, 50.0f);
		
		gl2.glMatrixMode(GL2.GL_MODELVIEW);
		gl2.glLoadIdentity();
		
		// Clear the window with current clearing color
		gl2.glClear(GL2.GL_COLOR_BUFFER_BIT | GL2.GL_DEPTH_BUFFER_BIT);
		gl2.glPushMatrix();
			gl2.glTranslatef(0.0f, 0.0f, -2.5f);
			gl2.glRotatef(yRot, 0.0f, 1.0f, 0.0f);
			toonDrawTorus(gl2, 0.35f, 0.15f, 50, 25, vLightDir);
		gl2.glPopMatrix();
		
		// Do the buffer swap
		gl2.glFlush();
		
		// Rotate 1/2 degree more each frame
		yRot += 0.5f;
	}
}